package com.djhu.springsimple.beans.context;

import com.djhu.springsimple.beans.context.event.ContextRefreshContext;
import com.djhu.springsimple.beans.context.lifecycle.DefaultLifecycleProcessor;
import com.djhu.springsimple.beans.context.lifecycle.LifecycleProcessor;
import com.djhu.springsimple.beans.core.ApplicationEvent;
import com.djhu.springsimple.beans.factory.BaseBeanFactory;
import com.djhu.springsimple.beans.factory.BeanPostProcess;
import com.djhu.springsimple.util.ObjectUtils;

import java.util.List;

/**
 * @author by zw on 2018/4/16.
 */
public abstract class BaseApplicationContext implements ApplicationContext {
    private static final String APPLICATION_EVENT_MULTICASTER_BEAN_NAME = "applicationEventMulticaster";
    private static final String APPLICATION_INIT_LIFECYCLEPROCESS_BEAN_NAME = "applicationLifecycleProcess";

    private BaseBeanFactory beanFactory;

    private String id = ObjectUtils.identityToString(this);

    private String displayName = ObjectUtils.identityToString(this);

    private long startDate = System.currentTimeMillis();

    private ApplicationEventMulticaster applicationEventMulticaster;

    private LifecycleProcessor lifecycleProcessor ;

    public BaseApplicationContext(BaseBeanFactory beanFactory) {
        this.beanFactory = beanFactory;
    }

    public synchronized void  refresh() throws Exception {
        initApplicationEventMulticaster();
        loadBeanDefinitions(beanFactory);
        registerBeanPostProcessors(beanFactory);
        onRefresh();
        finishRefresh();
    }

    private void finishRefresh() throws Exception {
        initLifecycleProcessor();
        getLifecycleProcessor().onRefresh();
        publishEvent(new ContextRefreshContext(this));
    }

    private void initLifecycleProcessor() throws Exception {
        BaseBeanFactory beanFactory = getBeanFactory();
        if (beanFactory.containsBean(APPLICATION_INIT_LIFECYCLEPROCESS_BEAN_NAME)) {
            this.lifecycleProcessor =
                    (LifecycleProcessor) beanFactory.getBean(APPLICATION_INIT_LIFECYCLEPROCESS_BEAN_NAME);
        }
        else {
            this.lifecycleProcessor = new DefaultLifecycleProcessor(beanFactory);
        }
    }


    protected void initApplicationEventMulticaster() throws Exception {
        BaseBeanFactory beanFactory = getBeanFactory();
        if (beanFactory.containsBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME)) {
            this.applicationEventMulticaster =
                    (ApplicationEventMulticaster) beanFactory.getBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME);
        }
        else {
            this.applicationEventMulticaster = new SimpleEventMulticaster(beanFactory);
        }
    }


    protected  void onRefresh(){
        beanFactory.preInstantiateSingletons();
    }

    protected  void registerBeanPostProcessors(BaseBeanFactory beanFactory) throws Exception {
        List<BeanPostProcess> beanPostProcesses = beanFactory.getBeansForType(BeanPostProcess.class);
        beanFactory.setBeanPostProcesses(beanPostProcesses);
    }

    protected abstract void loadBeanDefinitions(BaseBeanFactory beanFactory) throws Exception;

    @Override
    public void publishEvent(ApplicationEvent event) throws Exception {
        getApplicationEventMulticaster().multicastEvent(event);
    }

    @Override
    public Object getBean(String name) throws Exception {
        return beanFactory.getBean(name);
    }

    @Override
    public boolean containsBean(String name) {
        return beanFactory.containsBean(name);
    }

    @Override
    public String[] getbeanNamesfortype(Class classType) {
        return beanFactory.getbeanNamesfortype(classType);
    }

    @Override
    public List getBeansForType(Class type) throws Exception {
        return beanFactory.getBeansForType(type);
    }

    @Override
    public String getId() {
        return id;
    }

    @Override
    public long getStartupDate() {
        return this.startDate;
    }

    public ApplicationEventMulticaster getApplicationEventMulticaster() {
        return applicationEventMulticaster;
    }

    @Override
    public String getApplicationContextName() {
        return displayName;
    }

    public BaseBeanFactory getBeanFactory() {
        return beanFactory;
    }

    public void setBeanFactory(BaseBeanFactory beanFactory) {
        this.beanFactory = beanFactory;
    }

    public LifecycleProcessor getLifecycleProcessor() {
        return lifecycleProcessor;
    }
}